import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog, ttk
import os
import re
import time
import datetime
import shutil
import json
import hashlib
import threading
from tkinter.scrolledtext import ScrolledText
import tkinter.font as tkfont
from collections import defaultdict
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
import base64
import random
import string

class NotePad:
    def __init__(self, root):
        self.root = root
        self.root.title("增强版记事本")
        self.root.geometry("1200x700")
        self.root.config(bg='#F0F0F0')  # 设置主窗口背景色
        
        # 加密相关变量
        self.encryption_enabled = False  # 是否启用加密
        self.encryption_key = None  # 加密密钥
        
        # 存储配置
        self.config_file = os.path.join(os.path.expanduser("~"), ".enhanced_notepad_config.json")
        self.load_config()  # 加载配置（包括存储路径和备份设置）
        
        # 应用数据目录 - 从配置加载
        self.app_data_dir = self.config.get('storage_path', 
            os.path.join(os.path.expanduser("~"), ".enhanced_notepad"))
        self.notes_dir = os.path.join(self.app_data_dir, "notes")
        self.recycle_bin_dir = os.path.join(self.app_data_dir, "recycle_bin")
        self.backup_dir = os.path.join(self.app_data_dir, "backups")  # 备份目录
        self.init_data_dirs()
        
        # 备份设置 - 从配置加载
        self.backup_frequency = self.config.get('backup_frequency', 'daily')  # daily/weekly
        self.last_backup_time = self.config.get('last_backup_time', 0)
        
        # 剪贴板历史记录
        self.clipboard_history = []
        self.max_clipboard_items = 10  # 最多保存10条剪贴板历史
        
        # 撤销/重做最大步数
        self.max_undo_steps = 100
        
        # 当前查找参数
        self.find_pattern = ""
        self.match_case = False
        self.find_pos = tk.END
        
        # 初始化字体和颜色设置
        self.current_font = ("SimHei", 12, "normal")  # 字体名、大小、样式
        self.current_fg_color = "black"               # 文本前景色
        self.current_bg_color = "white"               # 文本背景色
        
        # 笔记组织相关变量
        self.current_folder = ""  # 当前文件夹路径
        self.current_file = None  # 当前打开的文件
        self.notes_metadata = defaultdict(dict)  # 笔记元数据：创建时间、修改时间等
        self.load_notes_metadata()  # 加载笔记元数据
        
        # 排序相关
        self.sort_by = "modified_time"  # 默认按修改时间排序
        self.sort_order = "desc"  # 默认降序（新→旧）
        
        # 创建主布局
        self.create_main_layout()
        
        # 是否已保存
        self.saved = True
        
        # 创建菜单
        self.create_menu()
        
        # 绑定文本变化事件和快捷键
        self.bind_events()
        
        # 初始化笔记列表
        self.refresh_notes_list()
        
        # 检查是否需要自动备份
        self.check_auto_backup()

    def init_data_dirs(self):
        """初始化数据目录"""
        for dir_path in [self.app_data_dir, self.notes_dir, self.recycle_bin_dir, self.backup_dir]:
            if not os.path.exists(dir_path):
                os.makedirs(dir_path)

    def load_config(self):
        """加载应用配置"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    self.config = json.load(f)
            else:
                self.config = {
                    'storage_path': os.path.join(os.path.expanduser("~"), ".enhanced_notepad"),
                    'backup_frequency': 'daily',
                    'last_backup_time': 0,
                    'encryption_enabled': False
                }
        except Exception as e:
            print(f"加载配置失败: {e}")
            self.config = {
                'storage_path': os.path.join(os.path.expanduser("~"), ".enhanced_notepad"),
                'backup_frequency': 'daily',
                'last_backup_time': 0,
                'encryption_enabled': False
            }

    def save_config(self):
        """保存应用配置"""
        try:
            self.config['storage_path'] = self.app_data_dir
            self.config['backup_frequency'] = self.backup_frequency
            self.config['last_backup_time'] = self.last_backup_time
            self.config['encryption_enabled'] = self.encryption_enabled
            
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(self.config, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存配置失败: {e}")
            messagebox.showerror("错误", f"保存配置失败: {str(e)}")

    # 加密相关功能
    def generate_key(self, password):
        """从密码生成加密密钥"""
        # 使用SHA-256哈希密码生成32字节密钥（AES-256需要32字节密钥）
        return hashlib.sha256(password.encode('utf-8')).digest()

    def encrypt_data(self, data):
        """使用AES-256-CBC加密数据"""
        if not self.encryption_key:
            return data.encode('utf-8')
            
        try:
            # 生成随机16字节IV（初始化向量）
            iv = os.urandom(16)
            
            # 填充数据以适应块大小
            padder = padding.PKCS7(128).padder()
            padded_data = padder.update(data.encode('utf-8')) + padder.finalize()
            
            # 创建加密器并加密
            cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend())
            encryptor = cipher.encryptor()
            encrypted_data = encryptor.update(padded_data) + encryptor.finalize()
            
            # 返回IV + 加密数据（使用base64编码便于存储）
            return base64.b64encode(iv + encrypted_data)
        except Exception as e:
            print(f"加密失败: {e}")
            return data.encode('utf-8')

    def decrypt_data(self, encrypted_data):
        """使用AES-256-CBC解密数据"""
        if not self.encryption_key or not encrypted_data:
            return encrypted_data.decode('utf-8') if isinstance(encrypted_data, bytes) else encrypted_data
            
        try:
            # 解码base64数据
            data = base64.b64decode(encrypted_data)
            
            # 提取IV（前16字节）和实际加密数据
            iv = data[:16]
            cipher_text = data[16:]
            
            # 创建解密器并解密
            cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend())
            decryptor = cipher.decryptor()
            decrypted_data = decryptor.update(cipher_text) + decryptor.finalize()
            
            # 移除填充
            unpadder = padding.PKCS7(128).unpadder()
            unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
            
            return unpadded_data.decode('utf-8')
        except Exception as e:
            print(f"解密失败: {e}")
            messagebox.showerror("解密错误", "无法解密文件，可能是密码错误或文件已损坏")
            return ""

    def set_encryption_password(self):
        """设置或修改加密密码"""
        if self.encryption_enabled:
            # 修改密码
            current_pwd = simpledialog.askstring("当前密码", "请输入当前密码:", show='*')
            if not current_pwd:
                return
                
            # 验证当前密码
            test_key = self.generate_key(current_pwd)
            if test_key != self.encryption_key:
                messagebox.showerror("错误", "当前密码不正确")
                return
        
        # 输入新密码
        new_pwd = simpledialog.askstring("设置密码", "请输入新密码 (至少8位):", show='*')
        if not new_pwd or len(new_pwd) < 8:
            messagebox.showerror("错误", "密码长度必须至少8位")
            return
            
        confirm_pwd = simpledialog.askstring("确认密码", "请再次输入密码:", show='*')
        if new_pwd != confirm_pwd:
            messagebox.showerror("错误", "两次输入的密码不一致")
            return
            
        # 生成并保存密钥
        self.encryption_key = self.generate_key(new_pwd)
        self.encryption_enabled = True
        
        # 保存加密状态
        self.save_config()
        self.update_encryption_status()
        
        # 提示用户备份密钥
        if messagebox.askyesno("重要提示", "请备份您的密钥文件，否则忘记密码将无法恢复数据！是否现在备份？"):
            self.backup_encryption_key()
            
        messagebox.showinfo("成功", "密码设置成功，所有笔记将被加密存储")

    def backup_encryption_key(self):
        """备份加密密钥"""
        if not self.encryption_key:
            return
            
        try:
            # 生成随机文件名
            random_suffix = ''.join(random.choices(string.ascii_letters + string.digits, k=8))
            default_filename = f"notepad_key_backup_{random_suffix}.key"
            
            # 让用户选择保存位置
            file_path = filedialog.asksaveasfilename(
                defaultextension=".key",
                filetypes=[("密钥文件", "*.key"), ("所有文件", "*.*")],
                initialfile=default_filename
            )
            
            if file_path:
                # 保存密钥
                with open(file_path, 'wb') as f:
                    f.write(base64.b64encode(self.encryption_key))
                
                messagebox.showinfo("成功", f"密钥已备份到:\n{file_path}\n请妥善保管此文件！")
        except Exception as e:
            print(f"备份密钥失败: {e}")
            messagebox.showerror("错误", f"备份密钥失败: {str(e)}")

    def load_encryption_key(self):
        """从文件加载加密密钥"""
        file_path = filedialog.askopenfilename(
            filetypes=[("密钥文件", "*.key"), ("所有文件", "*.*")]
        )
        
        if file_path:
            try:
                with open(file_path, 'rb') as f:
                    key_data = f.read()
                    self.encryption_key = base64.b64decode(key_data)
                    self.encryption_enabled = True
                    self.save_config()
                    self.update_encryption_status()
                    messagebox.showinfo("成功", "密钥加载成功")
                    return True
            except Exception as e:
                print(f"加载密钥失败: {e}")
                messagebox.showerror("错误", f"加载密钥失败: {str(e)}")
                return False
        return False

    def unlock_with_password(self):
        """使用密码解锁加密数据"""
        password = simpledialog.askstring("解锁笔记", "请输入密码解锁笔记:", show='*')
        if password:
            self.encryption_key = self.generate_key(password)
            # 尝试解密一个测试文件来验证密码
            if self.test_encryption_key():
                self.encryption_enabled = True
                self.save_config()
                self.update_encryption_status()
                return True
            else:
                self.encryption_key = None
                messagebox.showerror("错误", "密码不正确")
                return False
        return False

    def test_encryption_key(self):
        """测试加密密钥是否正确"""
        # 尝试解密第一个找到的笔记文件
        for root, _, files in os.walk(self.notes_dir):
            for file in files:
                if file.endswith(('.txt', '.md', '.html')):
                    try:
                        file_path = os.path.join(root, file)
                        with open(file_path, 'rb') as f:
                            data = f.read()
                        # 尝试解密
                        self.decrypt_data(data)
                        return True
                    except:
                        continue
        # 如果没有找到文件或解密失败
        return False

    def toggle_encryption(self):
        """切换加密功能开关"""
        if self.encryption_enabled:
            if messagebox.askyesno("确认", "关闭加密功能将导致所有笔记以明文存储，确定要关闭吗？"):
                self.encryption_enabled = False
                self.encryption_key = None
                self.save_config()
                self.update_encryption_status()
                messagebox.showinfo("提示", "加密功能已关闭，新保存的笔记将不再加密")
        else:
            self.set_encryption_password()

    def update_encryption_status(self):
        """更新加密状态显示"""
        enc_status_text = "已加密" if self.encryption_enabled else "未加密"
        enc_status_color = "green" if self.encryption_enabled else "red"
        self.encryption_status.config(text=f"数据状态: {enc_status_text}", foreground=enc_status_color)

    # 备份相关功能
    def check_auto_backup(self):
        """检查是否需要执行自动备份"""
        now = time.time()
        days_since_backup = (now - self.last_backup_time) / (24 * 3600)
        
        # 根据备份频率检查是否需要备份
        if (self.backup_frequency == 'daily' and days_since_backup >= 1) or \
           (self.backup_frequency == 'weekly' and days_since_backup >= 7):
            # 在后台线程执行备份，不阻塞UI
            threading.Thread(target=self.perform_backup, daemon=True).start()

    def perform_backup(self):
        """执行数据备份"""
        try:
            # 创建带时间戳的备份目录
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            backup_path = os.path.join(self.backup_dir, f"backup_{timestamp}")
            
            # 复制笔记目录
            shutil.copytree(self.notes_dir, os.path.join(backup_path, "notes"))
            
            # 记录备份时间
            self.last_backup_time = time.time()
            self.save_config()
            
            # 清理旧备份（保留最近5个）
            self.cleanup_old_backups()
            
            # 显示备份完成提示（在主线程中）
            self.root.after(0, lambda: messagebox.showinfo(
                "备份完成", 
                f"自动备份已完成，备份保存至:\n{backup_path}"
            ))
        except Exception as e:
            print(f"备份失败: {e}")
            # 显示备份失败提示（在主线程中）
            self.root.after(0, lambda: messagebox.showerror(
                "备份失败", 
                f"自动备份失败: {str(e)}"
            ))

    def cleanup_old_backups(self, keep=5):
        """清理旧备份，只保留最近的几个"""
        try:
            # 获取所有备份目录并按时间排序
            backups = []
            for item in os.listdir(self.backup_dir):
                item_path = os.path.join(self.backup_dir, item)
                if os.path.isdir(item_path) and item.startswith("backup_"):
                    try:
                        timestamp_str = item[len("backup_"):]
                        timestamp = datetime.datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S").timestamp()
                        backups.append((timestamp, item_path))
                    except:
                        continue
            
            # 按时间排序（旧→新）
            backups.sort()
            
            # 删除多余的备份
            if len(backups) > keep:
                for timestamp, path in backups[:-keep]:
                    shutil.rmtree(path, ignore_errors=True)
        except Exception as e:
            print(f"清理旧备份失败: {e}")

    def manual_backup(self):
        """手动执行备份"""
        if messagebox.askyesno("确认", "是否立即执行手动备份？"):
            # 在后台线程执行备份
            threading.Thread(target=self.perform_backup, daemon=True).start()
            messagebox.showinfo("提示", "备份已开始，请等待完成通知")

    def restore_from_backup(self):
        """从备份恢复数据"""
        # 显示所有可用备份
        try:
            backups = []
            for item in os.listdir(self.backup_dir):
                item_path = os.path.join(self.backup_dir, item)
                if os.path.isdir(item_path) and item.startswith("backup_"):
                    try:
                        timestamp_str = item[len("backup_"):]
                        timestamp = datetime.datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S")
                        backups.append((timestamp, item_path))
                    except:
                        continue
            
            if not backups:
                messagebox.showinfo("提示", "没有找到可用的备份")
                return
                
            # 按时间排序（新→旧）
            backups.sort(reverse=True)
            
            # 创建备份选择窗口
            backup_window = tk.Toplevel(self.root)
            backup_window.title("选择备份")
            backup_window.geometry("400x300")
            backup_window.transient(self.root)
            
            ttk.Label(backup_window, text="请选择要恢复的备份:").pack(anchor=tk.W, padx=10, pady=10)
            
            backup_listbox = tk.Listbox(backup_window)
            backup_listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
            
            for i, (timestamp, path) in enumerate(backups):
                backup_listbox.insert(tk.END, f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
                backup_listbox.itemconfig(i, values=[path])
            
            def do_restore():
                selection = backup_listbox.curselection()
                if not selection:
                    messagebox.showinfo("提示", "请选择一个备份")
                    return
                    
                if messagebox.askyesno("警告", "恢复备份将覆盖当前所有笔记，确定要继续吗？"):
                    index = selection[0]
                    backup_path = backups[index][1]
                    source_notes = os.path.join(backup_path, "notes")
                    
                    # 先备份当前数据到临时目录
                    temp_backup = os.path.join(self.backup_dir, f"pre_restore_backup_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}")
                    shutil.copytree(self.notes_dir, os.path.join(temp_backup, "notes"))
                    
                    # 恢复选中的备份
                    shutil.rmtree(self.notes_dir)
                    shutil.copytree(source_notes, self.notes_dir)
                    
                    # 重新加载元数据和界面
                    self.load_notes_metadata()
                    self.refresh_folder_tree()
                    self.refresh_notes_list()
                    
                    backup_window.destroy()
                    messagebox.showinfo("成功", "数据已从备份恢复")
            
            btn_frame = ttk.Frame(backup_window)
            btn_frame.pack(fill=tk.X, padx=10, pady=10)
            
            ttk.Button(btn_frame, text="恢复选中", command=do_restore).pack(side=tk.LEFT, padx=5)
            ttk.Button(btn_frame, text="取消", command=backup_window.destroy).pack(side=tk.RIGHT, padx=5)
            
        except Exception as e:
            print(f"恢复备份失败: {e}")
            messagebox.showerror("错误", f"恢复备份失败: {str(e)}")

    def show_backup_settings(self):
        """显示备份设置对话框"""
        backup_window = tk.Toplevel(self.root)
        backup_window.title("备份设置")
        backup_window.geometry("300x200")
        backup_window.transient(self.root)
        backup_window.resizable(False, False)
        
        ttk.Label(backup_window, text="备份频率:").pack(anchor=tk.W, padx=10, pady=10)
        
        freq_var = tk.StringVar(value=self.backup_frequency)
        ttk.Radiobutton(backup_window, text="每天", variable=freq_var, value="daily").pack(anchor=tk.W, padx=20)
        ttk.Radiobutton(backup_window, text="每周", variable=freq_var, value="weekly").pack(anchor=tk.W, padx=20)
        
        ttk.Button(backup_window, text="立即备份", command=self.manual_backup).pack(fill=tk.X, padx=20, pady=10)
        ttk.Button(backup_window, text="从备份恢复", command=self.restore_from_backup).pack(fill=tk.X, padx=20, pady=5)
        
        def save_settings():
            self.backup_frequency = freq_var.get()
            self.save_config()
            backup_window.destroy()
        
        ttk.Button(backup_window, text="保存设置", command=save_settings).pack(fill=tk.X, padx=20, pady=10)

    # 存储路径设置
    def change_storage_path(self):
        """更改存储路径"""
        if not self.saved:
            if messagebox.askyesno("提示", "当前笔记有未保存内容，是否保存？"):
                self.save_note()
        
        new_path = filedialog.askdirectory(title="选择新的存储目录")
        if new_path:
            try:
                # 确认目录有效
                test_file = os.path.join(new_path, "test_write.txt")
                with open(test_file, 'w') as f:
                    f.write("test")
                os.remove(test_file)
                
                # 询问是否移动现有数据
                move_data = messagebox.askyesno("移动数据", "是否将现有笔记数据移动到新目录？")
                
                # 创建新目录结构
                new_notes_dir = os.path.join(new_path, "notes")
                new_recycle_dir = os.path.join(new_path, "recycle_bin")
                new_backup_dir = os.path.join(new_path, "backups")
                
                for dir_path in [new_path, new_notes_dir, new_recycle_dir, new_backup_dir]:
                    if not os.path.exists(dir_path):
                        os.makedirs(dir_path)
                
                # 移动现有数据
                if move_data:
                    self._move_directory_contents(self.notes_dir, new_notes_dir)
                    self._move_directory_contents(self.recycle_bin_dir, new_recycle_dir)
                    self._move_directory_contents(self.backup_dir, new_backup_dir)
                
                # 更新路径设置
                self.app_data_dir = new_path
                self.notes_dir = new_notes_dir
                self.recycle_bin_dir = new_recycle_dir
                self.backup_dir = new_backup_dir
                
                # 重新加载数据
                self.load_notes_metadata()
                self.refresh_folder_tree()
                self.refresh_notes_list()
                self.current_file = None
                self.text_area.delete(1.0, tk.END)
                
                # 保存配置
                self.save_config()
                
                messagebox.showinfo("成功", f"存储路径已更改为:\n{new_path}")
            except Exception as e:
                print(f"更改存储路径失败: {e}")
                messagebox.showerror("错误", f"更改存储路径失败: {str(e)}")

    def _move_directory_contents(self, src_dir, dst_dir):
        """移动目录内容的辅助函数"""
        for item in os.listdir(src_dir):
            src = os.path.join(src_dir, item)
            dst = os.path.join(dst_dir, item)
            
            if os.path.exists(dst):
                # 处理重名
                name, ext = os.path.splitext(item)
                counter = 1
                while os.path.exists(dst):
                    dst = os.path.join(dst_dir, f"{name}_{counter}{ext}")
                    counter += 1
            
            shutil.move(src, dst)

    def create_main_layout(self):
        """创建主界面布局"""
        # 创建主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 左侧笔记组织面板
        left_panel = ttk.Frame(main_frame, width=250)
        left_panel.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 5))
        left_panel.pack_propagate(False)
        
        # 文件夹树状视图
        ttk.Label(left_panel, text="文件夹").pack(anchor=tk.W, padx=5, pady=5)
        
        # 文件夹操作按钮
        folder_buttons = ttk.Frame(left_panel)
        folder_buttons.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Button(folder_buttons, text="新建", width=6, command=self.create_new_folder).pack(side=tk.LEFT, padx=2)
        ttk.Button(folder_buttons, text="重命名", width=6, command=self.rename_folder).pack(side=tk.LEFT, padx=2)
        ttk.Button(folder_buttons, text="删除", width=6, command=self.delete_folder).pack(side=tk.LEFT, padx=2)
        
        # 文件夹树
        self.folder_tree = ttk.Treeview(left_panel)
        self.folder_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=(0, 5))
        self.folder_tree.bind("<<TreeviewSelect>>", self.on_folder_select)
        
        # 笔记列表
        ttk.Label(left_panel, text="笔记列表").pack(anchor=tk.W, padx=5, pady=5)
        
        # 笔记操作按钮
        note_buttons = ttk.Frame(left_panel)
        note_buttons.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Button(note_buttons, text="新建", width=6, command=self.new_note).pack(side=tk.LEFT, padx=2)
        ttk.Button(note_buttons, text="排序", width=6, command=self.show_sort_options).pack(side=tk.LEFT, padx=2)
        ttk.Button(note_buttons, text="批量", width=6, command=self.batch_operations).pack(side=tk.LEFT, padx=2)
        
        # 笔记列表视图
        self.notes_listbox = tk.Listbox(left_panel, selectmode=tk.EXTENDED)
        self.notes_listbox.pack(fill=tk.BOTH, expand=True, padx=5)
        self.notes_listbox.bind("<Double-1>", self.on_note_double_click)
        
        # 加密状态显示
        enc_status_text = "已加密" if self.encryption_enabled else "未加密"
        enc_status_color = "green" if self.encryption_enabled else "red"
        self.encryption_status = ttk.Label(
            left_panel, 
            text=f"数据状态: {enc_status_text}",
            foreground=enc_status_color
        )
        self.encryption_status.pack(fill=tk.X, padx=5, pady=5)
        
        # 回收站按钮
        ttk.Button(left_panel, text="回收站", command=self.show_recycle_bin).pack(fill=tk.X, padx=5, pady=5)
        
        # 右侧编辑区域
        right_panel = ttk.Frame(main_frame)
        right_panel.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
        # 文本编辑区域，支持撤销/重做和列选模式
        self.text_area = ScrolledText(
            right_panel, 
            undo=True,
            maxundo=self.max_undo_steps,
            wrap=tk.WORD,
            font=self.current_font,
            fg=self.current_fg_color,
            bg=self.current_bg_color,
            relief=tk.SUNKEN,
            bd=2
        )
        # 支持列选模式 (按住Alt键拖动)
        self.text_area.bind("<Alt-ButtonPress-1>", self.start_column_select)
        self.text_area.bind("<Alt-B1-Motion>", self.update_column_select)
        self.text_area.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
        
        # 初始化文件夹树
        self.refresh_folder_tree()

    def refresh_folder_tree(self):
        """刷新文件夹树状视图"""
        # 清空现有内容
        for item in self.folder_tree.get_children():
            self.folder_tree.delete(item)
        
        # 添加根节点
        root_node = self.folder_tree.insert("", tk.END, text="我的笔记", open=True)
        
        # 递归添加文件夹
        self.add_folders_to_tree("", root_node)

    def add_folders_to_tree(self, parent_path, tree_parent, tree=None):
        """递归添加文件夹到树状视图"""
        if tree is None:
            tree = self.folder_tree
            
        full_parent_path = os.path.join(self.notes_dir, parent_path)
        
        if not os.path.exists(full_parent_path):
            return
            
        for item in os.listdir(full_parent_path):
            item_path = os.path.join(parent_path, item)
            full_item_path = os.path.join(self.notes_dir, item_path)
            
            if os.path.isdir(full_item_path):
                # 添加文件夹节点
                node = tree.insert(tree_parent, tk.END, text=item, values=[item_path])
                # 递归添加子文件夹
                self.add_folders_to_tree(item_path, node, tree)

    def load_notes_metadata(self):
        """加载笔记元数据"""
        # 清空现有元数据
        self.notes_metadata.clear()
        
        # 遍历所有笔记文件
        for root, dirs, files in os.walk(self.notes_dir):
            relative_root = os.path.relpath(root, self.notes_dir)
            if relative_root == ".":
                relative_root = ""
                
            for file in files:
                file_path = os.path.join(relative_root, file)
                full_path = os.path.join(self.notes_dir, file_path)
                
                # 获取文件时间信息
                create_time = os.path.getctime(full_path)
                modified_time = os.path.getmtime(full_path)
                
                # 尝试获取标题（第一行内容）
                title = self._get_note_title(full_path, file)
                
                self.notes_metadata[file_path] = {
                    "title": title,
                    "create_time": create_time,
                    "modified_time": modified_time,
                    "path": file_path,
                    "folder": relative_root
                }

    def _get_note_title(self, file_path, default_title):
        """获取笔记标题的辅助函数"""
        try:
            with open(file_path, "rb") as f:
                content = f.read()
            
            # 如果启用了加密，先解密
            if self.encryption_enabled:
                decrypted_content = self.decrypt_data(content)
            else:
                decrypted_content = content.decode('utf-8')
                
            return decrypted_content.split('\n')[0].strip() or default_title
        except Exception as e:
            print(f"读取文件 {default_title} 元数据失败: {e}")
            return default_title

    def refresh_notes_list(self):
        """刷新笔记列表"""
        # 清空列表
        self.notes_listbox.delete(0, tk.END)
        
        # 获取当前文件夹下的笔记
        current_notes = [
            meta for path, meta in self.notes_metadata.items()
            if meta["folder"] == self.current_folder
        ]
        
        # 排序笔记
        sorted_notes = self.sort_notes(current_notes)
        
        # 添加到列表
        for note in sorted_notes:
            display_text = f"{note['title']}  ({datetime.datetime.fromtimestamp(note['modified_time']).strftime('%Y-%m-%d')})"
            self.notes_listbox.insert(tk.END, display_text)
            # 存储完整路径作为列表项的额外数据
            self.notes_listbox.itemconfig(self.notes_listbox.size() - 1, 
                                        values=[note['path']])

    def sort_notes(self, notes):
        """根据当前排序方式对笔记进行排序"""
        if self.sort_by == "create_time":
            sorted_notes = sorted(notes, key=lambda x: x["create_time"])
        elif self.sort_by == "modified_time":
            sorted_notes = sorted(notes, key=lambda x: x["modified_time"])
        elif self.sort_by == "title":
            sorted_notes = sorted(notes, key=lambda x: x["title"].lower())
        else:  # 默认按修改时间
            sorted_notes = sorted(notes, key=lambda x: x["modified_time"])
            
        # 处理排序顺序
        if self.sort_order == "desc":
            sorted_notes = sorted_notes[::-1]
            
        return sorted_notes

    def on_folder_select(self, event):
        """处理文件夹选择事件"""
        selected_items = self.folder_tree.selection()
        if selected_items:
            item = selected_items[0]
            # 获取文件夹路径
            self.current_folder = self.folder_tree.item(item, "values")[0] if self.folder_tree.item(item, "values") else ""
            # 刷新笔记列表
            self.refresh_notes_list()

    def on_note_double_click(self, event):
        """处理笔记双击事件"""
        selected_index = self.notes_listbox.curselection()
        if selected_index:
            # 获取选中的笔记索引
            index = selected_index[0]
            # 获取笔记路径
            note_title = self.notes_listbox.get(index).split("  (")[0]
            current_notes = [meta for path, meta in self.notes_metadata.items() if meta["folder"] == self.current_folder]
            sorted_notes = self.sort_notes(current_notes)
            
            for note in sorted_notes:
                if note["title"] == note_title:
                    self.open_note_from_path(note["path"])
                    break

    def open_note_from_path(self, note_path):
        """从路径打开笔记"""
        # 如果启用了加密但未解锁，提示用户输入密码
        if self.encryption_enabled and not self.encryption_key:
            if not self.unlock_with_password():
                return
                
        if not self.saved:
            if messagebox.askyesno("提示", "当前笔记有未保存内容，是否保存？"):
                self.save_note()
                
        full_path = os.path.join(self.notes_dir, note_path)
        if os.path.exists(full_path):
            self.current_file = note_path
            try:
                with open(full_path, "rb") as file:
                    content = file.read()
                
                # 如果启用了加密，解密内容
                if self.encryption_enabled:
                    decrypted_content = self.decrypt_data(content)
                else:
                    decrypted_content = content.decode('utf-8')
                
                self.text_area.delete(1.0, tk.END)
                self.text_area.insert(tk.END, decrypted_content)
                self.saved = True
                self.root.title(f"增强版记事本 - {os.path.basename(note_path)}")
            except Exception as e:
                print(f"打开文件失败: {e}")
                messagebox.showerror("错误", f"打开文件失败: {str(e)}")

    def create_new_folder(self):
        """创建新文件夹"""
        folder_name = simpledialog.askstring("新建文件夹", "请输入文件夹名称:")
        if folder_name and folder_name.strip():
            # 获取当前选中的文件夹
            selected_items = self.folder_tree.selection()
            parent_path = ""
            
            if selected_items:
                item = selected_items[0]
                parent_path = self.folder_tree.item(item, "values")[0] if self.folder_tree.item(item, "values") else ""
            
            # 创建文件夹路径
            new_folder_path = os.path.join(self.notes_dir, parent_path, folder_name)
            
            if not os.path.exists(new_folder_path):
                os.makedirs(new_folder_path)
                self.refresh_folder_tree()
            else:
                messagebox.showerror("错误", "文件夹已存在！")

    def rename_folder(self):
        """重命名文件夹"""
        selected_items = self.folder_tree.selection()
        if not selected_items:
            messagebox.showinfo("提示", "请先选择一个文件夹")
            return
            
        item = selected_items[0]
        old_path = self.folder_tree.item(item, "values")[0] if self.folder_tree.item(item, "values") else ""
        
        # 不能重命名根目录
        if not old_path:
            messagebox.showinfo("提示", "不能重命名根目录")
            return
            
        new_name = simpledialog.askstring("重命名文件夹", "请输入新名称:")
        if new_name and new_name.strip():
            old_full_path = os.path.join(self.notes_dir, old_path)
            parent_dir = os.path.dirname(old_full_path)
            new_full_path = os.path.join(parent_dir, new_name)
            
            if not os.path.exists(new_full_path):
                os.rename(old_full_path, new_full_path)
                
                # 更新笔记元数据中的路径
                old_folder = old_path
                new_folder = os.path.join(os.path.dirname(old_path), new_name)
                
                for path in list(self.notes_metadata.keys()):
                    if self.notes_metadata[path]["folder"].startswith(old_folder):
                        # 更新路径
                        new_note_path = path.replace(old_folder, new_folder, 1)
                        self.notes_metadata[new_note_path] = self.notes_metadata[path]
                        self.notes_metadata[new_note_path]["path"] = new_note_path
                        self.notes_metadata[new_note_path]["folder"] = new_folder
                        del self.notes_metadata[path]
                
                self.refresh_folder_tree()
                self.refresh_notes_list()
            else:
                messagebox.showerror("错误", "文件夹已存在！")

    def delete_folder(self):
        """删除文件夹"""
        selected_items = self.folder_tree.selection()
        if not selected_items:
            messagebox.showinfo("提示", "请先选择一个文件夹")
            return
            
        item = selected_items[0]
        folder_path = self.folder_tree.item(item, "values")[0] if self.folder_tree.item(item, "values") else ""
        
        # 不能删除根目录
        if not folder_path:
            messagebox.showinfo("提示", "不能删除根目录")
            return
            
        if messagebox.askyesno("确认", f"确定要删除文件夹 '{folder_path}' 及其所有内容吗？"):
            full_path = os.path.join(self.notes_dir, folder_path)
            
            # 移动到回收站
            recycle_path = os.path.join(self.recycle_bin_dir, f"{int(time.time())}_{os.path.basename(full_path)}")
            shutil.move(full_path, recycle_path)
            
            # 记录删除时间
            with open(os.path.join(recycle_path, ".delete_info"), "w") as f:
                f.write(str(time.time()))
            
            # 更新元数据
            for path in list(self.notes_metadata.keys()):
                if path.startswith(folder_path):
                    del self.notes_metadata[path]
            
            self.refresh_folder_tree()
            self.refresh_notes_list()

    def show_sort_options(self):
        """显示排序选项对话框"""
        sort_window = tk.Toplevel(self.root)
        sort_window.title("排序选项")
        sort_window.geometry("300x200")
        sort_window.transient(self.root)
        sort_window.resizable(False, False)
        
        # 排序方式
        ttk.Label(sort_window, text="排序方式:").grid(row=0, column=0, sticky=tk.W, padx=10, pady=10)
        
        sort_by_var = tk.StringVar(value=self.sort_by)
        ttk.Radiobutton(sort_window, text="创建时间", variable=sort_by_var, value="create_time").grid(row=0, column=1, sticky=tk.W)
        ttk.Radiobutton(sort_window, text="修改时间", variable=sort_by_var, value="modified_time").grid(row=1, column=1, sticky=tk.W)
        ttk.Radiobutton(sort_window, text="标题", variable=sort_by_var, value="title").grid(row=2, column=1, sticky=tk.W)
        
        # 排序顺序
        ttk.Label(sort_window, text="排序顺序:").grid(row=3, column=0, sticky=tk.W, padx=10, pady=10)
        
        sort_order_var = tk.StringVar(value=self.sort_order)
        ttk.Radiobutton(sort_window, text="新→旧 / A→Z", variable=sort_order_var, value="desc").grid(row=3, column=1, sticky=tk.W)
        ttk.Radiobutton(sort_window, text="旧→新 / Z→A", variable=sort_order_var, value="asc").grid(row=4, column=1, sticky=tk.W)
        
        # 应用按钮
        def apply_sort():
            self.sort_by = sort_by_var.get()
            self.sort_order = sort_order_var.get()
            self.refresh_notes_list()
            sort_window.destroy()
        
        ttk.Button(sort_window, text="应用", command=apply_sort).grid(row=5, column=0, columnspan=2, pady=20)

    def batch_operations(self):
        """批量操作对话框"""
        selected_indices = self.notes_listbox.curselection()
        if not selected_indices:
            messagebox.showinfo("提示", "请先选择要操作的笔记")
            return
            
        # 获取选中的笔记
        current_notes = [meta for path, meta in self.notes_metadata.items() if meta["folder"] == self.current_folder]
        sorted_notes = self.sort_notes(current_notes)
        selected_notes = [sorted_notes[i] for i in selected_indices]
        
        batch_window = tk.Toplevel(self.root)
        batch_window.title("批量操作")
        batch_window.geometry("400x300")
        batch_window.transient(self.root)
        
        ttk.Label(batch_window, text=f"已选择 {len(selected_notes)} 个笔记").pack(pady=10)
        
        # 移动按钮
        def move_notes():
            # 选择目标文件夹
            folder_window = tk.Toplevel(batch_window)
            folder_window.title("选择目标文件夹")
            folder_window.geometry("300x400")
            folder_window.transient(batch_window)
            
            ttk.Label(folder_window, text="选择目标文件夹:").pack(anchor=tk.W, padx=5, pady=5)
            
            folder_tree = ttk.Treeview(folder_window)
            folder_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
            
            # 添加根节点
            root_node = folder_tree.insert("", tk.END, text="我的笔记", open=True)
            self.add_folders_to_tree("", root_node, folder_tree)
            
            def confirm_move():
                selected_folder = folder_tree.selection()
                if selected_folder:
                    item = selected_folder[0]
                    target_folder = folder_tree.item(item, "values")[0] if folder_tree.item(item, "values") else ""
                    
                    for note in selected_notes:
                        old_path = os.path.join(self.notes_dir, note["path"])
                        new_path = os.path.join(self.notes_dir, target_folder, os.path.basename(note["path"]))
                        
                        # 处理重名
                        counter = 1
                        base_new_path = new_path
                        while os.path.exists(new_path):
                            name, ext = os.path.splitext(base_new_path)
                            new_path = f"{name}_{counter}{ext}"
                            counter += 1
                        
                        os.rename(old_path, new_path)
                        
                        # 更新元数据
                        del self.notes_metadata[note["path"]]
                        new_note_relative_path = os.path.join(target_folder, os.path.basename(new_path))
                        self.notes_metadata[new_note_relative_path] = note
                        self.notes_metadata[new_note_relative_path]["path"] = new_note_relative_path
                        self.notes_metadata[new_note_relative_path]["folder"] = target_folder
                        self.notes_metadata[new_note_relative_path]["modified_time"] = time.time()
                    
                    self.refresh_notes_list()
                    folder_window.destroy()
                    batch_window.destroy()
            
            ttk.Button(folder_window, text="确认移动", command=confirm_move).pack(pady=10)
        
        ttk.Button(batch_window, text="移动到其他文件夹", command=move_notes).pack(fill=tk.X, padx=20, pady=5)
        
        # 删除按钮
        def delete_notes():
            if messagebox.askyesno("确认", f"确定要删除选中的 {len(selected_notes)} 个笔记吗？"):
                for note in selected_notes:
                    note_path = os.path.join(self.notes_dir, note["path"])
                    
                    # 移动到回收站
                    file_name = os.path.basename(note_path)
                    recycle_path = os.path.join(self.recycle_bin_dir, f"{int(time.time())}_{file_name}")
                    shutil.move(note_path, recycle_path)
                    
                    # 记录删除时间
                    with open(os.path.join(recycle_path, ".delete_info"), "w") as f:
                        f.write(str(time.time()))
                    
                    # 更新元数据
                    del self.notes_metadata[note["path"]]
                
                self.refresh_notes_list()
                batch_window.destroy()
        
        ttk.Button(batch_window, text="删除选中笔记", command=delete_notes).pack(fill=tk.X, padx=20, pady=5)
        
        # 取消按钮
        ttk.Button(batch_window, text="取消", command=batch_window.destroy).pack(
            fill=tk.X, padx=20, pady=20, side=tk.BOTTOM)

    def show_recycle_bin(self):
        """显示回收站"""
        recycle_window = tk.Toplevel(self.root)
        recycle_window.title("回收站")
        recycle_window.geometry("600x400")
        recycle_window.transient(self.root)
        
        ttk.Label(recycle_window, text="已删除的项目（默认保留30天）").pack(anchor=tk.W, padx=10, pady=10)
        
        # 回收站列表
        recycle_listbox = tk.Listbox(recycle_window, selectmode=tk.EXTENDED)
        recycle_listbox.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 加载回收站内容
        recycled_items = []
        for item in os.listdir(self.recycle_bin_dir):
            item_path = os.path.join(self.recycle_bin_dir, item)
            if os.path.isfile(item_path) or os.path.isdir(item_path):
                # 获取删除时间
                delete_time = 0
                info_file = os.path.join(item_path, ".delete_info") if os.path.isdir(item_path) else f"{item_path}.delete_info"
                
                if os.path.exists(info_file):
                    with open(info_file, "r") as f:
                        delete_time = float(f.read())
                
                recycled_items.append((item, item_path, delete_time))
        
        # 按删除时间排序（最新的在前面）
        recycled_items.sort(key=lambda x: x[2], reverse=True)
        
        # 添加到列表
        for item, path, delete_time in recycled_items:
            delete_date = datetime.datetime.fromtimestamp(delete_time).strftime('%Y-%m-%d')
            recycle_listbox.insert(tk.END, f"{item}  (删除于: {delete_date})")
        
        # 恢复按钮
        def restore_selected():
            selected_indices = recycle_listbox.curselection()
            if not selected_indices:
                messagebox.showinfo("提示", "请先选择要恢复的项目")
                return
                
            for i in selected_indices:
                item_name, item_path, _ = recycled_items[i]
                
                # 确定恢复路径
                target_path = os.path.join(self.notes_dir, item_name)
                counter = 1
                base_target_path = target_path
                
                while os.path.exists(target_path):
                    name, ext = os.path.splitext(base_target_path)
                    target_path = f"{name}_{counter}{ext}"
                    counter += 1
                
                shutil.move(item_path, target_path)
                
                # 如果是文件夹，恢复其中的笔记元数据
                if os.path.isdir(target_path):
                    # 重新扫描恢复的文件夹以更新元数据
                    self._update_metadata_from_directory(target_path)
                else:
                    # 单个文件恢复
                    self._update_metadata_from_file(target_path)
            
            # 刷新列表
            self.refresh_folder_tree()
            self.refresh_notes_list()
            recycle_window.destroy()
        
        ttk.Button(recycle_window, text="恢复选中项目", command=restore_selected).pack(side=tk.LEFT, padx=10, pady=10)
        
        # 清空回收站按钮
        def empty_recycle_bin():
            if messagebox.askyesno("确认", "确定要清空回收站吗？此操作不可恢复！"):
                for item in os.listdir(self.recycle_bin_dir):
                    item_path = os.path.join(self.recycle_bin_dir, item)
                    try:
                        if os.path.isfile(item_path) or os.path.islink(item_path):
                            os.unlink(item_path)
                        elif os.path.isdir(item_path):
                            shutil.rmtree(item_path)
                    except Exception as e:
                        print(f"删除失败: {e}")
                
                recycle_listbox.delete(0, tk.END)
        
        ttk.Button(recycle_window, text="清空回收站", command=empty_recycle_bin).pack(side=tk.RIGHT, padx=10, pady=10)
        
        # 底部按钮框
        btn_frame = ttk.Frame(recycle_window)
        btn_frame.pack(fill=tk.X, side=tk.BOTTOM)

    def _update_metadata_from_directory(self, directory):
        """从目录更新元数据的辅助函数"""
        for root, _, files in os.walk(directory):
            relative_root = os.path.relpath(root, self.notes_dir)
            for file in files:
                if file == ".delete_info":
                    continue
                    
                file_path = os.path.join(relative_root, file)
                full_path = os.path.join(self.notes_dir, file_path)
                
                create_time = os.path.getctime(full_path)
                modified_time = os.path.getmtime(full_path)
                
                title = self._get_note_title(full_path, file)
                
                self.notes_metadata[file_path] = {
                    "title": title,
                    "create_time": create_time,
                    "modified_time": modified_time,
                    "path": file_path,
                    "folder": relative_root
                }

    def _update_metadata_from_file(self, file_path):
        """从单个文件更新元数据的辅助函数"""
        relative_path = os.path.relpath(file_path, self.notes_dir)
        folder = os.path.dirname(relative_path)
        
        create_time = os.path.getctime(file_path)
        modified_time = os.path.getmtime(file_path)
        
        title = self._get_note_title(file_path, os.path.basename(file_path))
        
        self.notes_metadata[relative_path] = {
            "title": title,
            "create_time": create_time,
            "modified_time": modified_time,
            "path": relative_path,
            "folder": folder
        }

    def create_menu(self):
        menubar = tk.Menu(self.root)
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="新建空白笔记", command=self.new_note, accelerator="Ctrl+N")
        file_menu.add_command(label="新建模板笔记", command=self.new_template_note)
        file_menu.add_separator()
        file_menu.add_command(label="打开笔记", command=self.open_note, accelerator="Ctrl+O")
        file_menu.add_separator()
        file_menu.add_command(label="保存", command=self.save_note, accelerator="Ctrl+S")
        file_menu.add_command(label="另存为", command=self.save_note_as)
        file_menu.add_separator()
        
        # 存储管理子菜单
        storage_menu = tk.Menu(file_menu, tearoff=0)
        storage_menu.add_command(label="更改存储位置", command=self.change_storage_path)
        storage_menu.add_command(label="备份设置", command=self.show_backup_settings)
        file_menu.add_cascade(label="存储管理", menu=storage_menu)
        
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.exit_app, accelerator="Ctrl+Q")
        
        # 编辑菜单
        edit_menu = tk.Menu(menubar, tearoff=0)
        # 撤销/重做子菜单
        undo_redo_menu = tk.Menu(edit_menu, tearoff=0)
        undo_redo_menu.add_command(label="撤销", command=self.undo, accelerator="Ctrl+Z")
        undo_redo_menu.add_command(label="重做", command=self.redo, accelerator="Ctrl+Y")
        edit_menu.add_cascade(label="撤销/重做", menu=undo_redo_menu)
        edit_menu.add_separator()
        
        # 剪贴板子菜单
        clipboard_menu = tk.Menu(edit_menu, tearoff=0)
        clipboard_menu.add_command(label="剪切", command=self.cut_text, accelerator="Ctrl+X")
        clipboard_menu.add_command(label="复制", command=self.copy_text, accelerator="Ctrl+C")
        clipboard_menu.add_command(label="粘贴", command=self.paste_text, accelerator="Ctrl+V")
        clipboard_menu.add_separator()
        clipboard_menu.add_command(label="剪贴板历史", command=self.show_clipboard_history)
        edit_menu.add_cascade(label="剪贴板操作", menu=clipboard_menu)
        edit_menu.add_separator()
        
        # 选择子菜单
        select_menu = tk.Menu(edit_menu, tearoff=0)
        select_menu.add_command(label="全选", command=self.select_all, accelerator="Ctrl+A")
        select_menu.add_command(label="列选模式", command=self.show_column_select_help)
        edit_menu.add_cascade(label="选择", menu=select_menu)
        edit_menu.add_separator()
        
        # 查找替换
        edit_menu.add_command(label="查找/替换", command=self.show_find_replace, accelerator="Ctrl+F")
        
        # 格式菜单
        format_menu = tk.Menu(menubar, tearoff=0)
        # 文本样式子菜单
        style_menu = tk.Menu(format_menu, tearoff=0)
        style_menu.add_command(label="加粗", command=lambda: self.apply_format("bold"), accelerator="Ctrl+B")
        style_menu.add_command(label="斜体", command=lambda: self.apply_format("italic"), accelerator="Ctrl+I")
        style_menu.add_command(label="下划线", command=lambda: self.apply_format("underline"), accelerator="Ctrl+U")
        style_menu.add_command(label="删除线", command=lambda: self.apply_format("overstrike"))
        format_menu.add_cascade(label="文本样式", menu=style_menu)
        format_menu.add_separator()
        
        # 字体设置
        font_menu = tk.Menu(format_menu, tearoff=0)
        font_menu.add_command(label="字体设置", command=self.set_font)
        font_menu.add_command(label="颜色设置", command=self.set_color)
        format_menu.add_cascade(label="字体与颜色", menu=font_menu)
        
        # 笔记管理菜单
        note_menu = tk.Menu(menubar, tearoff=0)
        note_menu.add_command(label="新建文件夹", command=self.create_new_folder)
        note_menu.add_command(label="排序笔记", command=self.show_sort_options)
        note_menu.add_command(label="批量操作", command=self.batch_operations)
        note_menu.add_separator()
        
        # 加密相关菜单
        encryption_menu = tk.Menu(note_menu, tearoff=0)
        encryption_menu.add_command(label="设置密码/修改密码", command=self.set_encryption_password)
        encryption_menu.add_command(label="加载密钥文件", command=self.load_encryption_key)
        encryption_menu.add_command(label="备份密钥", command=self.backup_encryption_key)
        encryption_menu.add_separator()
        encryption_menu.add_command(label="开启/关闭加密", command=self.toggle_encryption)
        note_menu.add_cascade(label="数据加密", menu=encryption_menu)
        
        note_menu.add_separator()
        note_menu.add_command(label="显示回收站", command=self.show_recycle_bin)
        
        menubar.add_cascade(label="文件", menu=file_menu)
        menubar.add_cascade(label="编辑", menu=edit_menu)
        menubar.add_cascade(label="格式", menu=format_menu)
        menubar.add_cascade(label="笔记管理", menu=note_menu)
        self.root.config(menu=menubar)

    def bind_events(self):
        """绑定事件和快捷键"""
        # 文本变化事件
        self.text_area.bind("<Key>", self.on_text_change)
        
        # 快捷键绑定
        self.root.bind("<Control-n>", lambda event: self.new_note())
        self.root.bind("<Control-o>", lambda event: self.open_note())
        self.root.bind("<Control-s>", lambda event: self.save_note())
        self.root.bind("<Control-q>", lambda event: self.exit_app())
        self.root.bind("<Control-z>", lambda event: self.undo())
        self.root.bind("<Control-y>", lambda event: self.redo())
        self.root.bind("<Control-x>", lambda event: self.cut_text())
        self.root.bind("<Control-c>", lambda event: self.copy_text())
        self.root.bind("<Control-v>", lambda event: self.paste_text())
        self.root.bind("<Control-a>", lambda event: self.select_all())
        self.root.bind("<Control-f>", lambda event: self.show_find_replace())
        self.root.bind("<Control-b>", lambda event: self.apply_format("bold"))
        self.root.bind("<Control-i>", lambda event: self.apply_format("italic"))
        self.root.bind("<Control-u>", lambda event: self.apply_format("underline"))

    def show_column_select_help(self):
        """显示列选模式帮助信息"""
        messagebox.showinfo("列选模式", "列选模式：按住Alt键并拖动鼠标即可垂直选择文本，适合编辑表格或代码。")

    # 基础编辑工具实现
    def undo(self):
        """撤销操作"""
        try:
            self.text_area.edit_undo()
            self.saved = False
        except tk.TclError:
            pass  # 没有可撤销的操作

    def redo(self):
        """重做操作"""
        try:
            self.text_area.edit_redo()
            self.saved = False
        except tk.TclError:
            pass  # 没有可重做的操作

    def cut_text(self):
        """剪切选中的文本"""
        try:
            # 获取选中的文本
            selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            # 保存到剪贴板历史
            if selected_text:
                self.add_to_clipboard_history(selected_text)
            # 执行剪切
            self.text_area.event_generate("<<Cut>>")
            self.saved = False
        except tk.TclError:
            messagebox.showinfo("提示", "没有选中的文本")

    def copy_text(self):
        """复制选中的文本"""
        try:
            # 获取选中的文本
            selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
            # 保存到剪贴板历史
            if selected_text:
                self.add_to_clipboard_history(selected_text)
            # 执行复制
            self.text_area.event_generate("<<Copy>>")
        except tk.TclError:
            messagebox.showinfo("提示", "没有选中的文本")

    def paste_text(self):
        """粘贴文本"""
        self.text_area.event_generate("<<Paste>>")
        self.saved = False

    def add_to_clipboard_history(self, text):
        """添加文本到剪贴板历史"""
        # 避免重复添加相同内容
        if self.clipboard_history and self.clipboard_history[0] == text:
            return
            
        # 添加到历史记录开头
        self.clipboard_history.insert(0, text)
        
        # 保持历史记录长度不超过最大值
        if len(self.clipboard_history) > self.max_clipboard_items:
            self.clipboard_history = self.clipboard_history[:self.max_clipboard_items]

    def show_clipboard_history(self):
        """显示剪贴板历史记录"""
        if not self.clipboard_history:
            messagebox.showinfo("剪贴板历史", "剪贴板为空")
            return
            
        # 创建剪贴板历史窗口
        history_window = tk.Toplevel(self.root)
        history_window.title("剪贴板历史")
        history_window.geometry("400x300")
        history_window.transient(self.root)
        history_window.grab_set()
        
        # 创建列表框显示历史记录
        listbox = tk.Listbox(history_window, selectmode=tk.SINGLE)
        listbox.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
        
        # 添加历史记录到列表框
        for i, item in enumerate(self.clipboard_history):
            # 限制显示长度
            display_text = item[:50] + ("..." if len(item) > 50 else "")
            listbox.insert(tk.END, f"{i+1}. {display_text}")
        
        # 双击条目粘贴
        def paste_selected(event):
            index = listbox.curselection()
            if index:
                text = self.clipboard_history[index[0]]
                self.text_area.insert(tk.INSERT, text)
                self.saved = False
                history_window.destroy()
        
        listbox.bind("<Double-1>", paste_selected)
        
        # 按钮
        btn_frame = ttk.Frame(history_window)
        btn_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Button(btn_frame, text="粘贴选中", 
                  command=lambda: paste_selected(None)).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="清空历史", 
                  command=lambda: [self.clipboard_history.clear(), history_window.destroy()]).pack(side=tk.RIGHT, padx=5)

    def select_all(self):
        """全选文本"""
        self.text_area.tag_add(tk.SEL, "1.0", tk.END)
        self.text_area.mark_set(tk.INSERT, "1.0")
        self.text_area.see(tk.INSERT)
        return 'break'

    def start_column_select(self, event):
        """开始列选模式"""
        self.text_area.tag_remove("sel", "1.0", tk.END)
        self.start_row = self.text_area.index(f"@{event.x},{event.y}").split('.')[0]
        self.start_col = self.text_area.index(f"@{event.x},{event.y}").split('.')[1]
        self.text_area.mark_set("insert", f"@{event.x},{event.y}")
        return "break"

    def update_column_select(self, event):
        """更新列选区域"""
        self.text_area.tag_remove("sel", "1.0", tk.END)
        end_pos = self.text_area.index(f"@{event.x},{event.y}")
        end_row = end_pos.split('.')[0]
        end_col = end_pos.split('.')[1]
        
        # 确定列选范围
        start_row = min(int(self.start_row), int(end_row))
        end_row = max(int(self.start_row), int(end_row))
        start_col = min(int(self.start_col), int(end_col))
        end_col = max(int(self.start_col), int(end_col))
        
        # 选中列区域
        for row in range(start_row, end_row + 1):
            self.text_area.tag_add("sel", f"{row}.{start_col}", f"{row}.{end_col}")
        return "break"

    # 查找与替换功能实现
    def show_find_replace(self):
        """显示查找替换对话框"""
        # 创建对话框
        find_window = tk.Toplevel(self.root)
        find_window.title("查找与替换")
        find_window.geometry("400x200")
        find_window.transient(self.root)
        find_window.resizable(False, False)
        
        # 创建控件
        ttk.Label(find_window, text="查找内容:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
        find_entry = ttk.Entry(find_window, width=30)
        find_entry.grid(row=0, column=1, padx=5, pady=5)
        find_entry.focus_set()
        
        ttk.Label(find_window, text="替换为:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
        replace_entry = ttk.Entry(find_window, width=30)
        replace_entry.grid(row=1, column=1, padx=5, pady=5)
        
        # 匹配大小写复选框
        match_case_var = tk.BooleanVar(value=self.match_case)
        match_case_check = ttk.Checkbutton(find_window, text="匹配大小写", variable=match_case_var)
        match_case_check.grid(row=2, column=0, columnspan=2, sticky=tk.W, padx=5)
        
        # 按钮区域
        btn_frame = ttk.Frame(find_window)
        btn_frame.grid(row=3, column=0, columnspan=2, pady=10)
        
        # 查找下一个按钮
        def find_next():
            self.match_case = match_case_var.get()
            self.find_pattern = find_entry.get()
            if not self.find_pattern:
                messagebox.showinfo("提示", "请输入查找内容")
                return
                
            self.find_text()
        
        # 替换按钮
        def replace():
            self.match_case = match_case_var.get()
            self.find_pattern = find_entry.get()
            replace_text = replace_entry.get()
            
            if not self.find_pattern:
                messagebox.showinfo("提示", "请输入查找内容")
                return
                
            # 检查是否有选中的匹配项
            try:
                selected_text = self.text_area.get(tk.SEL_FIRST, tk.SEL_LAST)
                if selected_text == self.find_pattern or (not self.match_case and selected_text.lower() == self.find_pattern.lower()):
                    self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
                    self.text_area.insert(tk.SEL_FIRST, replace_text)
                    self.saved = False
            except tk.TclError:
                pass
                
            # 查找下一个
            self.find_text()
        
        # 全部替换按钮
        def replace_all():
            self.match_case = match_case_var.get()
            self.find_pattern = find_entry.get()
            replace_text = replace_entry.get()
            
            if not self.find_pattern:
                messagebox.showinfo("提示", "请输入查找内容")
                return
                
            # 从开头开始查找
            self.find_pos = "1.0"
            count = 0
            
            # 循环查找并替换所有匹配项
            while True:
                result = self.find_text(automatic=True)
                if not result:
                    break
                    
                # 替换找到的文本
                self.text_area.delete(tk.SEL_FIRST, tk.SEL_LAST)
                self.text_area.insert(tk.SEL_FIRST, replace_text)
                self.saved = False
                count += 1
                self.find_pos = tk.SEL_FIRST  # 从替换后的位置继续查找
                
            messagebox.showinfo("替换完成", f"共替换了 {count} 处匹配")
        
        ttk.Button(btn_frame, text="查找下一个", command=find_next).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="替换", command=replace).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="全部替换", command=replace_all).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="取消", command=find_window.destroy).pack(side=tk.LEFT, padx=5)

    def find_text(self, automatic=False):
        """查找文本并高亮显示"""
        # 清除之前的选中
        self.text_area.tag_remove(tk.SEL, "1.0", tk.END)
        
        if not self.find_pattern:
            return False
            
        # 获取文本内容
        text_content = self.text_area.get("1.0", tk.END)
        
        # 确定查找的起始位置
        start_pos = self.find_pos if self.find_pos != tk.END else "1.0"
        
        # 根据是否匹配大小写调整搜索模式
        if self.match_case:
            pattern = re.escape(self.find_pattern)
        else:
            pattern = re.escape(self.find_pattern)
            text_content_lower = text_content.lower()
            find_pattern_lower = self.find_pattern.lower()
        
        # 计算起始位置的偏移量
        start_index = self.text_area.index(start_pos)
        start_line, start_col = map(int, start_index.split('.'))
        start_offset = 0
        
        # 计算从开始到起始位置的字符数
        for i in range(1, start_line):
            line_end = self.text_area.index(f"{i}.end")
            line_length = int(line_end.split('.')[1]) + 1  # +1 是因为包含换行符
            start_offset += line_length
        start_offset += start_col
        
        # 执行查找
        found = False
        if self.match_case:
            match = re.search(pattern, text_content[start_offset:])
        else:
            match = re.search(pattern, text_content_lower[start_offset:], re.IGNORECASE)
        
        if match:
            # 计算匹配位置
            start = start_offset + match.start()
            end = start_offset + match.end()
            
            # 将偏移量转换为文本索引
            current_offset = 0
            start_index = "1.0"
            end_index = "1.0"
            
            line = 1
            while True:
                line_end = self.text_area.index(f"{line}.end")
                line_length = int(line_end.split('.')[1]) + 1  # +1 是因为包含换行符
                
                if current_offset + line_length > start:
                    col = start - current_offset
                    start_index = f"{line}.{col}"
                    break
                    
                current_offset += line_length
                line += 1
                
                # 防止无限循环
                if line > 100000:  # 限制最大行数
                    break
            
            line = 1
            current_offset = 0
            while True:
                line_end = self.text_area.index(f"{line}.end")
                line_length = int(line_end.split('.')[1]) + 1  # +1 是因为包含换行符
                
                if current_offset + line_length > end:
                    col = end - current_offset
                    end_index = f"{line}.{col}"
                    break
                    
                current_offset += line_length
                line += 1
                
                # 防止无限循环
                if line > 100000:  # 限制最大行数
                    break
            
            # 选中找到的文本
            self.text_area.tag_add(tk.SEL, start_index, end_index)
            self.text_area.mark_set(tk.INSERT, end_index)
            self.text_area.see(tk.INSERT)
            self.find_pos = end_index
            found = True
        else:
            # 如果没找到且不是自动替换模式，提示用户
            if not automatic:
                messagebox.showinfo("查找完成", f"找不到 '{self.find_pattern}'")
                self.find_pos = "1.0"  # 回到开始位置
        
        return found

    # 格式调整功能实现
    def apply_format(self, format_type):
        """应用文本格式（加粗、斜体等）"""
        try:
            # 获取选中的文本范围
            start = self.text_area.index(tk.SEL_FIRST)
            end = self.text_area.index(tk.SEL_LAST)
            
            # 检查是否已有该格式
            current_tags = self.text_area.tag_names(f"{start}+1c")
            
            # 根据格式类型设置或移除标签
            if format_type in current_tags:
                self.text_area.tag_remove(format_type, start, end)
            else:
                # 确保标签已配置
                if format_type == "bold":
                    self.text_area.tag_configure("bold", font=(self.current_font[0], self.current_font[1], "bold"))
                elif format_type == "italic":
                    self.text_area.tag_configure("italic", font=(self.current_font[0], self.current_font[1], "italic"))
                elif format_type == "underline":
                    self.text_area.tag_configure("underline", underline=True)
                elif format_type == "overstrike":
                    self.text_area.tag_configure("overstrike", overstrike=True)
                
                self.text_area.tag_add(format_type, start, end)
            
            self.saved = False
        except tk.TclError:
            messagebox.showinfo("提示", "请先选中要设置格式的文本")

    def set_font(self):
        """设置字体、字号和样式"""
        # 创建字体设置窗口
        font_window = tk.Toplevel(self.root)
        font_window.title("字体设置")
        font_window.geometry("400x300")
        font_window.transient(self.root)
        font_window.resizable(False, False)
        font_window.config(bg='#F0F0F0')
        
        # 字体选择
        ttk.Label(font_window, text="字体:").grid(row=0, column=0, sticky=tk.W, padx=10, pady=10)
        font_families = sorted([f for f in tkfont.families() if 'sim' in f.lower() or 'microsoft' in f.lower() or 'song' in f.lower()])
        font_var = tk.StringVar(value=self.current_font[0])
        font_combo = ttk.Combobox(font_window, textvariable=font_var, values=font_families, width=25)
        font_combo.grid(row=0, column=1, padx=10, pady=10)
        
        # 字号选择
        ttk.Label(font_window, text="字号:").grid(row=1, column=0, sticky=tk.W, padx=10, pady=10)
        sizes = list(range(8, 73, 2))
        size_var = tk.IntVar(value=self.current_font[1])
        size_combo = ttk.Combobox(font_window, textvariable=size_var, values=sizes, width=10)
        size_combo.grid(row=1, column=1, padx=10, pady=10)
        
        # 字体样式选择
        ttk.Label(font_window, text="样式:").grid(row=2, column=0, sticky=tk.W, padx=10, pady=10)
        style_frame = ttk.Frame(font_window)
        style_frame.grid(row=2, column=1, sticky=tk.W, padx=10, pady=10)
        
        style_var = tk.StringVar(value=self.current_font[2])
        ttk.Radiobutton(style_frame, text="常规", variable=style_var, value="normal").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(style_frame, text="加粗", variable=style_var, value="bold").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(style_frame, text="斜体", variable=style_var, value="italic").pack(side=tk.LEFT, padx=5)
        
        # 应用按钮
        def apply_font():
            font_family = font_var.get()
            font_size = size_var.get()
            font_style = style_var.get()
            self.current_font = (font_family, font_size, font_style)
            
            # 更新文本区域字体
            font_config = (font_family, font_size)
            if font_style == "bold":
                font_config = (font_family, font_size, "bold")
            elif font_style == "italic":
                font_config = (font_family, font_size, "italic")
                
            self.text_area.configure(font=font_config)
            
            # 更新格式标签的字体
            self.text_area.tag_configure("bold", font=(font_family, font_size, "bold"))
            self.text_area.tag_configure("italic", font=(font_family, font_size, "italic"))
            
            self.saved = False
            font_window.destroy()
        
        btn_frame = ttk.Frame(font_window)
        btn_frame.grid(row=3, column=0, columnspan=2, pady=20)
        ttk.Button(btn_frame, text="应用", command=apply_font, width=15).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text="取消", command=font_window.destroy, width=15).pack(side=tk.LEFT, padx=10)

    def set_color(self):
        """设置文本颜色和背景颜色"""
        # 创建颜色选择窗口
        color_window = tk.Toplevel(self.root)
        color_window.title("颜色设置")
        color_window.geometry("500x400")
        color_window.transient(self.root)
        color_window.config(bg='#F0F0F0')
        
        # 颜色设置选项卡
        notebook = ttk.Notebook(color_window)
        notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 文本颜色选项卡
        fg_frame = ttk.Frame(notebook)
        notebook.add(fg_frame, text="文本颜色")
        
        # 背景颜色选项卡
        bg_frame = ttk.Frame(notebook)
        notebook.add(bg_frame, text="背景颜色")
        
        # 预定义一些常用颜色
        colors = [
            "black", "red", "green", "blue", "cyan", "magenta", "yellow", "white",
            "darkred", "darkgreen", "darkblue", "darkcyan", "darkmagenta", "darkyellow",
            "gray", "darkgray", "lightgray", "pink", "purple", "orange", "brown",
            "#FF4500", "#FFD700", "#32CD32", "#1E90FF", "#9370DB", "#FF69B4", "#20B2AA"
        ]
        
        # 文本颜色选择
        fg_color_var = tk.StringVar(value=self.current_fg_color)
        self.create_color_grid(fg_frame, colors, fg_color_var)
        
        # 背景颜色选择
        bg_color_var = tk.StringVar(value=self.current_bg_color)
        self.create_color_grid(bg_frame, colors, bg_color_var)
        
        # 应用按钮
        def apply_color():
            # 更新文本颜色
            self.current_fg_color = fg_color_var.get()
            # 更新背景颜色
            self.current_bg_color = bg_color_var.get()
            
            # 应用颜色设置
            self.text_area.configure(fg=self.current_fg_color, bg=self.current_bg_color)
            
            self.saved = False
            color_window.destroy()
        
        btn_frame = ttk.Frame(color_window)
        btn_frame.pack(fill=tk.X, padx=10, pady=10)
        ttk.Button(btn_frame, text="应用", command=apply_color, width=15).pack(side=tk.LEFT, padx=10)
        ttk.Button(btn_frame, text="取消", command=color_window.destroy, width=15).pack(side=tk.RIGHT, padx=10)

    def create_color_grid(self, parent, colors, color_var):
        """创建颜色选择网格"""
        # 颜色选择说明
        ttk.Label(parent, text="点击选择颜色:").pack(anchor=tk.W, padx=10, pady=10)
        
        # 颜色网格框架
        color_frame = ttk.Frame(parent)
        color_frame.pack(fill=tk.BOTH, expand=True, padx=10)
        
        # 创建颜色选择网格
        row, col = 0, 0
        for color in colors:
            # 颜色按钮框架
            btn_frame = ttk.Frame(color_frame, width=40, height=40, relief=tk.RAISED, borderwidth=2)
            btn_frame.grid(row=row, column=col, padx=5, pady=5)
            btn_frame.pack_propagate(False)
            
            # 颜色示例
            ttk.Label(btn_frame, background=color).pack(fill=tk.BOTH, expand=True)
            
            # 点击选择颜色
            def select_color(c):
                return lambda: color_var.set(c)
            
            btn_frame.bind("<Button-1>", select_color(color))
            
            col += 1
            if col > 6:  # 每行显示7个颜色
                col = 0
                row += 1

    # 文件操作功能
    def new_note(self):
        if not self.saved:
            if messagebox.askyesno("提示", "当前笔记有未保存内容，是否保存？"):
                self.save_note()
        self.text_area.delete(1.0, tk.END)
        self.current_file = None
        self.saved = True
        self.root.title("增强版记事本 - 未保存笔记")

    def new_template_note(self):
        template = simpledialog.askstring("选择模板", "请输入模板类型（待办/日记）：")
        if template:
            if not self.saved:
                if messagebox.askyesno("提示", "当前笔记有未保存内容，是否保存？"):
                    self.save_note()
            self.text_area.delete(1.0, tk.END)
            if template.lower() == "待办":
                self.text_area.insert(tk.END, "## 待办事项\n- [ ] 任务1\n- [ ] 任务2\n")
            elif template.lower() == "日记":
                today = datetime.date.today().strftime("%Y-%m-%d")
                self.text_area.insert(tk.END, f"# {today} 日记\n\n今天发生了...\n")
            self.current_file = None
            self.saved = False
            self.root.title("增强版记事本 - 未保存笔记")

    def open_note(self):
        if not self.saved:
            if messagebox.askyesno("提示", "当前笔记有未保存内容，是否保存？"):
                self.save_note()
        
        # 如果启用了加密但未解锁，提示用户输入密码
        if self.encryption_enabled and not self.encryption_key:
            if not self.unlock_with_password():
                return
                
        file_path = filedialog.askopenfilename(
            filetypes=[("文本文件", "*.txt"), ("Markdown文件", "*.md"), ("HTML文件", "*.html"), ("所有文件", "*.*")]
        )
        if file_path:
            # 检查是否在笔记目录中
            if file_path.startswith(self.notes_dir):
                self.current_file = os.path.relpath(file_path, self.notes_dir)
            else:
                self.current_file = file_path
                
            try:
                with open(file_path, "rb") as file:
                    content = file.read()
                
                # 如果启用了加密，解密内容
                if self.encryption_enabled:
                    decrypted_content = self.decrypt_data(content)
                else:
                    decrypted_content = content.decode('utf-8')
                
                self.text_area.delete(1.0, tk.END)
                self.text_area.insert(tk.END, decrypted_content)
                
                # 更新元数据
                if file_path.startswith(self.notes_dir):
                    create_time = os.path.getctime(file_path)
                    modified_time = os.path.getmtime(file_path)
                    
                    title = decrypted_content.split('\n')[0].strip() or os.path.basename(file_path)
                    
                    self.notes_metadata[self.current_file] = {
                        "title": title,
                        "create_time": create_time,
                        "modified_time": modified_time,
                        "path": self.current_file,
                        "folder": os.path.dirname(self.current_file)
                    }
                
                self.saved = True
                self.root.title(f"增强版记事本 - {os.path.basename(file_path)}")
                # 刷新列表
                self.refresh_notes_list()
            except Exception as e:
                print(f"打开文件失败: {e}")
                messagebox.showerror("错误", f"打开文件失败: {str(e)}")

    def save_note(self):
        if self.current_file and (self.current_file.startswith(self.notes_dir) or os.path.exists(os.path.join(self.notes_dir, self.current_file))):
            # 保存到现有笔记
            full_path = os.path.join(self.notes_dir, self.current_file) if not self.current_file.startswith(self.notes_dir) else self.current_file
            content = self.text_area.get(1.0, tk.END)
            
            try:
                # 如果启用了加密，加密内容
                if self.encryption_enabled:
                    encrypted_content = self.encrypt_data(content)
                else:
                    encrypted_content = content.encode('utf-8')
                
                with open(full_path, "wb") as file:
                    file.write(encrypted_content)
                
                # 更新元数据
                modified_time = time.time()
                title = content.split('\n')[0].strip() or os.path.basename(self.current_file)
                
                if self.current_file in self.notes_metadata:
                    self.notes_metadata[self.current_file]["modified_time"] = modified_time
                    self.notes_metadata[self.current_file]["title"] = title
                else:
                    create_time = os.path.getctime(full_path)
                    self.notes_metadata[self.current_file] = {
                        "title": title,
                        "create_time": create_time,
                        "modified_time": modified_time,
                        "path": self.current_file,
                        "folder": os.path.dirname(self.current_file)
                    }
                
                self.saved = True
                self.root.title(f"增强版记事本 - {os.path.basename(self.current_file)}")
                self.refresh_notes_list()
            except Exception as e:
                print(f"保存文件失败: {e}")
                messagebox.showerror("错误", f"保存文件失败: {str(e)}")
        else:
            self.save_note_as()

    def save_note_as(self):
        # 默认为当前文件夹
        initial_dir = os.path.join(self.notes_dir, self.current_folder) if self.current_folder else self.notes_dir
        
        file_types = [
            ("文本文件", "*.txt"),
            ("Markdown文件", "*.md"),
            ("HTML文件", "*.html")
        ]
        file_path = filedialog.asksaveasfilename(filetypes=file_types, initialdir=initial_dir)
        
        if file_path:
            try:
                content = self.text_area.get(1.0, tk.END)
                
                # 如果启用了加密，加密内容
                if self.encryption_enabled:
                    encrypted_content = self.encrypt_data(content)
                else:
                    encrypted_content = content.encode('utf-8')
                
                with open(file_path, "wb") as file:
                    file.write(encrypted_content)
                
                # 检查是否保存在笔记目录中
                if file_path.startswith(self.notes_dir):
                    self.current_file = os.path.relpath(file_path, self.notes_dir)
                    
                    # 更新元数据
                    create_time = os.path.getctime(file_path)
                    modified_time = os.path.getmtime(file_path)
                    
                    title = content.split('\n')[0].strip() or os.path.basename(file_path)
                    
                    self.notes_metadata[self.current_file] = {
                        "title": title,
                        "create_time": create_time,
                        "modified_time": modified_time,
                        "path": self.current_file,
                        "folder": os.path.dirname(self.current_file)
                    }
                    
                    # 刷新文件夹树和笔记列表
                    self.refresh_folder_tree()
                    self.refresh_notes_list()
                else:
                    self.current_file = file_path
                    
                self.saved = True
                self.root.title(f"增强版记事本 - {os.path.basename(file_path)}")
            except Exception as e:
                print(f"另存为失败: {e}")
                messagebox.showerror("错误", f"另存为失败: {str(e)}")

    def on_text_change(self, event):
        self.saved = False
        if self.current_file:
            self.root.title(f"增强版记事本 - {os.path.basename(self.current_file)} *")
        else:
            self.root.title("增强版记事本 - 未保存笔记 *")

    def exit_app(self):
        if not self.saved:
            if messagebox.askyesno("提示", "当前笔记有未保存内容，是否保存？"):
                self.save_note()
        self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    # 设置窗口图标（根据实际路径修改）
    # root.iconbitmap('favicon.ico')
    app = NotePad(root)
    root.mainloop()
    